/* 
 * Shuriken is the legal property of its developers. Please refer to the
 * COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices; 

namespace Shuriken.Core.Shell
{
    /// <summary>
    /// ShellManager is responsible for all shell related functionality 
    /// </summary>
    public class ShellManager
    {
        private static ShellManager m_shellManager = null;
        private ShellItem shDesktop = null;
        private SystemImageList sysImageList = null; 

        /// <summary>
        /// Default constructor is private, so noone can instantiate this class. 
        /// </summary>
        private ShellManager()
        {
            shDesktop = new ShellItem();
            SystemImageList.InitImageList();
        }

        /// <summary>
        /// Default instance of this class 
        /// </summary>
        public static ShellManager Instance
        {
            get
            {
                return GetDefaultInstance();
            }
        }

        /// <summary>
        /// Creating the default instance 
        /// </summary>
        /// <returns>default instance</returns>
        private static ShellManager GetDefaultInstance()
        {
            if (null == m_shellManager)
            {
                m_shellManager = new ShellManager();
            }

            return m_shellManager; 
        }

        /// <summary>
        /// Returns a list of all the items under the shell item passed in the parameters. 
        /// </summary>
        /// <param name="shItem">ShellItem to walk</param>
        /// <param name="list">item list</param>
        private void FlattentFolderHierarchy(ShellItem shItem, List<ShellItem> list)
        {
            if (list == null)
            {
                throw new NullReferenceException("List cannot be null");
            }

            List<ShellItem> folders = shItem.GetSubFolders();

            foreach (ShellItem item in folders)
            {
                FlattentFolderHierarchy(item, list);
            }

            list.AddRange(shItem.GetSubItems());
        }

        /// <summary>
        /// Retrieves the list of shell items from folder specified. 
        /// </summary>
        /// <param name="shDesktop">Desktop folder</param>
        /// <param name="class_id">Folder to get the list of items from</param>
        /// <param name="flat">if true it returns all the items below this folder</param>
        /// <returns>list of shell item</returns>
        private List<ShellItem> GetShellItemsList(ShellItem shDesktop, ShellAPI.CSIDL class_id, bool flat)
        {
            List<ShellItem> result = new List<ShellItem>();


            int hRes;
            System.IntPtr pIDL = IntPtr.Zero;

            hRes = ShellAPI.SHGetSpecialFolderLocation(IntPtr.Zero, class_id, ref pIDL);
            if (hRes != 0)
            {
                if (System.IntPtr.Zero != pIDL)
                {
                    Marshal.FreeCoTaskMem(pIDL);
                }

                Marshal.ThrowExceptionForHR(hRes);
            }

            ShellItem shShellItem = new ShellItem(shDesktop.RootShellFolder, pIDL, shDesktop);

            if (flat)
            {
                FlattentFolderHierarchy(shShellItem, result);
            }
            else
            {
                result.AddRange(shShellItem.GetAllSubItems());
            }

            Marshal.FreeCoTaskMem(pIDL);

            return result;
        }

        /// <summary>
        /// Retrieves the list of shell items from folder specified. 
        /// </summary>
        /// <param name="class_id">Folder to get the list of items from</param>
        /// <param name="flat">if true it returns all the items below this folder</param>
        /// <returns>list of shell item</returns>
        public List<ShellItem> GetShellItemsList(ShellAPI.CSIDL class_id, bool flat)
        {
            return GetShellItemsList(this.Desktop, class_id, flat);
        }

        /// <summary>
        /// Retrieves the list of folders and files from the root level
        /// </summary>
        /// <returns>list of items and folders</returns>
        public List<ShellItem> GetDesktopLevelShellItemList()
        {
            List<ShellItem> list = new List<ShellItem>();

            List<ShellItem> folders = shDesktop.GetSubFolders();
            list.AddRange(folders);

            List<ShellItem> files = shDesktop.GetSubItems();
            list.AddRange(files);

            return list;
        }

        /// <summary>
        /// Desktop shell item
        /// </summary>
        public ShellItem Desktop
        {
            get
            {
                return shDesktop; 
            }
        }

        /// <summary>
        /// Draws the given shell image to the graphics context
        /// </summary>
        /// <param name="g">Graphics context</param>
        /// <param name="imageIndex">image index</param>
        public void DrawShellImage(System.Drawing.Graphics g, int imageIndex)
        {
            DrawShellImage(g, 0, 0, imageIndex);
        }

        /// <summary>
        /// Draws the given shell image to the graphics context
        /// </summary>
        /// <param name="g">Graphics context</param>
        /// <param name="x">x position</param>
        /// <param name="y">y position</param>
        /// <param name="imageIndex">image index</param>
        internal void DrawShellImage(System.Drawing.Graphics g, int x, int y, int imageIndex)
        {
            DrawShellImage(g, x, y, 32, 32, imageIndex);
        }

        /// <summary>
        /// Draws the given shell image to the graphics context
        /// </summary>
        /// <param name="g">Graphics context</param>
        /// <param name="x">x position</param>
        /// <param name="y">y position</param>
        /// <param name="width">width of the image</param>
        /// <param name="height">height of the image</param>
        /// <param name="imageIndex">image index</param>
        internal void DrawShellImage(System.Drawing.Graphics g, int x, int y, int width, int height, int imageIndex)
        {
            SystemImageList.Draw(g, x, y, width, height, imageIndex);
        }

        /// <summary>
        /// Returns the system image index of the particular extension
        /// </summary>
        /// <param name="extension">extension e.g .txt or .png</param>
        /// <returns>system wide image index</returns>
        public int GetImageIndexForExtension(string extension)
        {
            Shuriken.Core.Shell.ShellAPI.SHFILEINFO shInfo = new Shuriken.Core.Shell.ShellAPI.SHFILEINFO();

            ShellAPI.SHGFI dwAttribs =
                ShellAPI.SHGFI.SHGFI_USEFILEATTRIBUTES |
                ShellAPI.SHGFI.SHGFI_LARGEICON |
                ShellAPI.SHGFI.SHGFI_SYSICONINDEX;

            Shuriken.Core.Shell.ShellAPI.SHGetFileInfo(extension, ShellAPI.FILE_ATTRIBUTE_NORMAL, out shInfo, (uint)Marshal.SizeOf(shInfo), dwAttribs);
            return shInfo.iIcon;
        }
    }
}
